﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using PloobsEngine.Modelo;

namespace PloobsEngine.SceneControl
{
    public class RenderHelper : IRenderHelper
    {
        public RenderHelper(EngineStuff engine)
        {
            this._engine = engine;
            quad.LoadContent(engine);
            effect = new BasicEffect(EngineStuff.GraphicsDevice, null);

        }

        #region IRender Members
        BasicEffect effect;
        private EngineStuff _engine;                
        private IDictionary<String, Texture2D> _dic = new Dictionary<String, Texture2D>();        
        private TextureRenderer quad = new TextureRenderer();


        public Microsoft.Xna.Framework.Graphics.Texture2D this[string scene]
        {
            get { return _dic[scene]; }
            set
            {
                _dic[scene] = value;
            }
        }

        #endregion               
        
        
        public void RenderSceneWithoutMaterial(IWorld _world, List<IObject> objListException, Matrix view, Matrix projection)
        {
            foreach (var obj in _world.Objects)
            {
                if (objListException.Contains(obj))
                    continue;

                Matrix MWorld = obj.getWorldMatrix();
                IModelo modelo = obj.Modelo;
                Matrix[] boneTransforms = modelo.getBonesTransformation();

                Matrix wld = obj.getWorldMatrix();
                for (int i = 0; i < modelo.MeshNumber; i++)
                {
                    BatchInformation[] bi = modelo.GetBatchInformation(i);
                    Matrix w1 = Matrix.Multiply(wld, boneTransforms[modelo.GetParentBoneIndex(i)]);
                    effect.World = w1;
                    effect.Projection = projection;
                    effect.View = view;
                    effect.EnableDefaultLighting();
                    
                    for (int j = 0; j < bi.Count(); j++)
                    {
                        effect.Begin();
                        EngineStuff.GraphicsDevice.VertexDeclaration = bi[j].VertexDeclaration;
                        EngineStuff.GraphicsDevice.Vertices[0].SetSource(modelo.GetVertexBuffer(i), bi[j].StreamOffset, bi[j].VertexStride);
                        EngineStuff.GraphicsDevice.Indices = modelo.GetIndexBuffer(i);

                        effect.CurrentTechnique.Passes[0].Begin();
                        EngineStuff.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, bi[j].BaseVertex, 0, bi[j].NumVertices, bi[j].StartIndex, bi[j].PrimitiveCount);
                        effect.CurrentTechnique.Passes[0].End();
                        effect.End();
                    }
                }
            }
        }

        public void RenderSceneWithoutMaterial(IWorld _world, Matrix view, Matrix projection)
        {

            foreach (var obj in _world.Objects)
            {

                Matrix MWorld = obj.getWorldMatrix();
                IModelo modelo = obj.Modelo;
                Matrix[] boneTransforms = modelo.getBonesTransformation();

                Matrix wld = obj.getWorldMatrix();
                for (int i = 0; i < modelo.MeshNumber; i++)
                {
                    BatchInformation[] bi = modelo.GetBatchInformation(i);
                    Matrix w1 = Matrix.Multiply(wld, boneTransforms[modelo.GetParentBoneIndex(i)]);
                    effect.World = w1;
                    effect.Projection = projection;
                    effect.View = view;
                    effect.EnableDefaultLighting();

                    for (int j = 0; j < bi.Count(); j++)
                    {
                        effect.Begin();
                        EngineStuff.GraphicsDevice.VertexDeclaration = bi[j].VertexDeclaration;
                        EngineStuff.GraphicsDevice.Vertices[0].SetSource(modelo.GetVertexBuffer(i), bi[j].StreamOffset, bi[j].VertexStride);
                        EngineStuff.GraphicsDevice.Indices = modelo.GetIndexBuffer(i);

                        effect.CurrentTechnique.Passes[0].Begin();
                        EngineStuff.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, bi[j].BaseVertex, 0, bi[j].NumVertices, bi[j].StartIndex, bi[j].PrimitiveCount);
                        effect.CurrentTechnique.Passes[0].End();
                        effect.End();
                    }
                }
            }
     }



        public void RenderTextureToFullScreenSpriteBatch(String texture, Effect effect,Rectangle destination)
        {            
            effect.Begin();
            EngineStuff.SpriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);

            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                EngineStuff.SpriteBatch.Draw(this[texture], destination, Color.White);
                pass.End();
            }
            EngineStuff.SpriteBatch.End();
            effect.End();
        }

       
        /// <summary>
        /// Auxiliar function to draw a quad using an effect
        /// </summary>
        /// <param name="texture"></param>
        /// <param name="effect"></param>
        public void RenderTextureToFullScreenSpriteBatch(Texture2D texture, Effect effect)
        {
            /// Its ordinary Xna Code - Very similar to Directx 
            effect.Begin();
            EngineStuff.SpriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);

            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                EngineStuff.SpriteBatch.Draw(texture, Vector2.Zero, Color.White);
                pass.End();
            }
            EngineStuff.SpriteBatch.End();
            effect.End();
        }





        #region IRenderHelper Members


        public void RenderTextureToFullScreenSpriteBatch(string scene, Effect effect)
        {
            this.RenderTextureToFullScreenSpriteBatch(this[scene], effect);
        }

        #endregion

        #region IRenderHelper Members


        public void RenderTextureToFullScreenVertexPixel(Effect effect)
        {
            quad.DrawQuad(effect);
        }
        
        #endregion

        #region IRenderHelper Members

        RenderTarget2D source = null;
        public void SetUpRenderTarget(RenderTarget2D source, RenderTarget2D target)
        {
            this.source = source;
            EngineStuff.GraphicsDevice.SetRenderTarget(0,target);
            EngineStuff.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1, 0);

        }

        public Texture2D GetResultsRenderTarget()
        {
            RenderTarget2D target =  EngineStuff.GraphicsDevice.GetRenderTarget(0) as RenderTarget2D;
            if (target == null)
                return null;
            EngineStuff.GraphicsDevice.SetRenderTarget(0, source);
            return target.GetTexture();

        }

        #endregion

        #region IRenderHelper Members


        public void SetUpRenderTarget(RenderTarget2D source, RenderTarget2D target, ClearOptions opts, Color color, float z, int stencil)
        {
            this.source = source;
            EngineStuff.GraphicsDevice.SetRenderTarget(0, target);
            EngineStuff.GraphicsDevice.Clear(opts, color, z, stencil);

        }

        #endregion

        #region IRenderHelper Members


        public void SetUpRenderTarget(RenderTarget2D target)
        {
            this.source = EngineStuff.GraphicsDevice.GetRenderTarget(0) as RenderTarget2D;            
            EngineStuff.GraphicsDevice.SetRenderTarget(0, target);
        }

        #endregion

        #region IRenderHelper Members


        public void RenderSceneWithoutMaterial(IWorld world, List<IObject> objListException, Matrix view, Matrix projection, bool drawComponentsPreDraw)
        {
            if(drawComponentsPreDraw)
             _engine.PreRender(EngineStuff.GetCurrentGameTime(),view,projection);
            RenderSceneWithoutMaterial(world, objListException, view, projection);
        }

        #endregion
    }
}

